<?php

namespace app\admins\controller;

use app\admins\model\Admin;
use app\admins\model\AdminGroup;
use app\admins\model\AdminMenu;
use app\admins\model\Setting;
use app\BaseController;

class Base extends BaseController
{
    protected $module;
    protected $controller;
    protected $action;
    protected $param;

    // 权限验证
    protected $auth;
    protected $admin;
    protected $admin_group;
    protected $admin_menu;

    // 设置相关
    protected $setting;
    protected $settings;

    // Curd 复用
    protected $model; //主表模型
    protected $field = []; //查询字段
    protected $map = []; //条件
    protected $sort = []; //排序
    protected $is_page = true; //是否分页
    protected $rule = []; //验证规则
    protected $allow = []; //验证字段

    // 初始化
    protected function initialize()
    {
        $this->module = $this->app->http->getName();
        $this->controller = $this->request->controller();
        $this->action = $this->request->action();
        $this->param = $this->request->param();

        // 权限表
        $this->admin = new Admin();
        $this->admin_group = new AdminGroup();
        $this->admin_menu = new AdminMenu();
        $this->setting = new Setting();
        $this->settings = $this->setting->select(); //减少数据查询复用

        // 禁止未登录用户进入系统
        $auth = $this->app->session->get('auth');
        if (!$auth) {
            $token = $this->app->cookie->get('auth');
            !$token && $this->errMsg(1, '请先登录', '/admins/account/login');
            $auth = $this->admin->where('remember_token', $token)->find();
            !$auth && $this->errMsg(1, '请先登录', '/admins/account/login');
            !session_id() && session_start();
            $this->app->session->set('auth', $auth);
        }

        // 检查单点登录
        $randstr = $this->app->cookie->get('randstr');
        $cache_randstr = $this->app->cache->get('rand' . $auth['id']);
        if ($this->setting('oneLogin') && $randstr !== $cache_randstr) {
            $this->app->session->delete('auth');
            $this->app->cookie->delete('auth');
            $this->errMsg(1, '您的账号已在别处登录，请重新登录！', '/admins/account/login');
        }

        // 获取当前菜单
        $menu = $this->admin_menu->where([
            ['controller', '=', $this->controller],
            ['action', '=', $this->action]
        ])->find();
        !$menu && $this->errMsg(1, '该功能不存在');

        // 菜单是否被禁用
        $menu['status'] && $this->errMsg(1, '该功能被禁用，请联系管理员');

        // 用户所在角色
        $group = $this->admin_group->where('id', $auth['gid'])->find();
        !$group && $this->errMsg(1, '该角色不存在，请联系管理员');

        // 该角色的权限菜单
        $rights = json_decode($group['rights'], true);
        !in_array($menu['id'], $rights) && $this->errMsg(1, '没有权限，请联系管理员');

        // 权限验证通过
        $auth['group_title'] = $group['title'];
        $auth['rights'] = $rights;
        $this->auth = $auth;
    }

    // 二维数组按键重组
    protected function formatKey($datas, $key = 'id', $column = null)
    {
        $res = [];
        foreach ($datas as $data) {
            if (is_object($data)) {
                $res[$data->$key] = $column ? $data->$column : $data;
            } else {
                $res[$data[$key]] = $column ? $data[$column] : $data;
            }
        }
        return $res;
    }

    // 无限层级递归
    protected function levels($datas, $pid = 0, $level = 0)
    {
        $res = [];
        foreach ($datas as $data) {
            if (is_object($data)) {
                if ($data->pid === $pid) {
                    $data->level = $level;
                    $data->children = $this->levels($datas, $data->id, $level + 1);
                    $res[] = $data;
                }
            } else {
                if ($data['pid'] === $pid) {
                    $data['level'] = $level;
                    $data['children'] = $this->levels($datas, $data['id'], $level + 1);
                    $res[] = $data;
                }
            }
        }
        return $res;
    }

    // 无限层级树
    protected function trees($datas, $pid = 0, $level = 0)
    {
        $res = array();
        foreach ($datas as $data) {
            if (is_object($data)) {
                if ($data->pid == $pid) {
                    $data->level = $level;
                    $res[] = $data;
                    $res = array_merge($res, $this->trees($datas, $data->id, $level + 1));
                }
            } else {
                if ($data['pid'] == $pid) {
                    $data['level'] = $level;
                    $res[] = $data;
                    $res = array_merge($res, $this->trees($datas, $data['id'], $level + 1));
                }
            }
        }
        return $res;
    }

    // 父子层级关系
    protected function parents($datas, $id = 0)
    {
        $res = array();
        foreach ($datas as $data) {
            if (is_object($data)) {
                if ($data->id == $id) {
                    $res[] = $data;
                    $res = array_merge($res, $this->parents($datas, $data->pid));
                }
            } else {
                if ($data['id'] == $id) {
                    $res[] = $data;
                    $res = array_merge($res, $this->parents($datas, $data['pid']));
                }
            }
        }
        return $res;
    }

    // 子数据平级
    protected function plates($datas, &$res = [])
    {
        foreach ($datas as $data) {
            if (is_object($data)) {
                if (isset($data->children)) {
                    $temp = $data->children;
                    unset($data->children);
                    $res[] = $data;
                    $this->plates($temp, $res);
                } else {
                    $res[] = $data;
                }
            } else {
                if (isset($data['children'])) {
                    $temp = $data['children'];
                    unset($data['children']);
                    $res[] = $data;
                    $this->plates($temp, $res);
                } else {
                    $res[] = $data;
                }
            }
        }
        return $res;
    }

    // 获取设置
    protected function setting($name, $field = 'base_setting')
    {
        $row = $this->settings->filter(function ($item) use ($field) {
            return $item['names'] === $field;
        })->shift();
        if ($row) {
            $values = $row['values'];
            $decode = json_decode($row['values'], true);
            $is_array = false;
            // 检查序列化或json格式
            if ($this->is_serialized($row['values'])) {
                $values = unserialize($row['values']);
                $is_array = true;
            } else if (!is_null($decode)) {
                $values = $decode;
                $is_array = true;
            }
            // 多字段情况
            if ($is_array) {
                if (isset($values[$name])) {
                    return $values[$name];
                }
                return false;
            } else {
                return $values; //单值情况
            }
        }
        return false;
    }

    // 是否序列化
    protected function is_serialized($data)
    {
        $data = trim($data);
        if ('N;' == $data)
            return true;
        if (!preg_match('/^([adObis]):/', $data, $badions))
            return false;
        if (in_array($badions[1], ['a', 'O', 's']) && preg_match("/^{$badions[1]}:[0-9]+:.*[;}]\$/s", $data))
            return true;
        if (in_array($badions[1], ['b', 'i', 'd']) && preg_match("/^{$badions[1]}:[0-9.E-]+;\$/", $data))
            return true;
        return false;
    }

    // 错误消息
    protected function errMsg($code = 1, $msg = 'Error!', $url = '', $data = [], $files = [])
    {
        if ($this->request->isAjax()) {
            echo json_encode(['code' => $code, 'msg' => $msg, 'url' => $url, 'data' => $data, 'files' => $files]);
        } else {
            echo '<p>&nbsp;</p><p style="text-align:center;color:#393D49;font-size:20px;">' . $msg . '</p>';
            if ($url)
                echo '<script>
                setTimeout(function () {
                    var _ta = window.location;
                    if (self.frameElement && self.frameElement.tagName == "IFRAME") {
                        _ta = parent.window.location;
                    }
                    _ta.href = "' . $url . '";
                }, 1000);
                </script>';
        }
        if ($code) exit;
    }
}

/**
 * Curd 复用（模型类）
 * $model; //主表模型
 * $field = []; //查询字段
 * $map = []; //初始条件
 * $sort = []; //排序
 * $is_page = true; //是否分页
 * $rule = []; //验证规则
 * $allow = []; //验证字段
 */
trait Curd
{
    protected $module;
    protected $controller;
    protected $action;
    protected $param;

    protected $model; //主表模型
    protected $field = []; //查询字段
    protected $map = []; //条件
    protected $sort = []; //排序
    protected $is_page = true; //是否分页
    protected $rule = []; //验证规则
    protected $allow = []; //验证字段

    // 列表
    public function index()
    {
        if ($this->request->isAjax()) {
            $param = $this->param ?? $this->request->param();
            // 搜索
            $map = $this->map;
            if (!empty($param['search'])) {
                foreach ((array)$param['search'] as $k => $v) {
                    if (strlen($v)) $map[] = [$k, 'like', "%{$v}%"];
                }
            }
            // 查询
            $list = $this->model->field($this->field)->where($map);
            $data['count'] = $list->count();
            if ($data['count']) {
                $data['code'] = 0;
            } else {
                $data['code'] = 1;
                $data['msg'] = '暂无记录';
            }
            // 分页
            if ($this->is_page) {
                $list = $list->page($param['page'], $param['limit']);
            }
            // 排序
            $list = $list->order($this->sort)->select();
            // 渲染
            $data['data'] = $this->datas($list);
            return $this->err($data);
        }
        return $this->app->view->fetch();
    }

    // 添加
    public function add()
    {
        return $this->app->view->fetch();
    }

    // 保存
    public function save()
    {
        // 验证
        $rule = $this->rule;
        $param = $this->param ?? $this->request->param();
        $rules = [];
        $allow = $this->allow ?: array_keys($param);
        foreach ($rule as $key => $value) {
            [$name,] = explode('|', $key);
            if (in_array($name, $allow)) {
                $rules[$key] = $value;
            }
        }
        $validate = $this->app->validate->rule($rules);
        !$validate->check($param) && $this->errMsg(1, $validate->getError());
        // 操作
        try {
            $save = $this->model->create($param);
        } catch (\Exception $e) {
            $this->err(['code' => 1, 'msg' => $e->getMessage()]);
        }
        $save ? $this->err(['code' => 0, 'msg' => '保存成功']) : $this->err(['code' => 1, 'msg' => '保存失败']);
    }

    // 编辑
    public function edit()
    {
        $param = $this->param ?? $this->request->param();
        empty($param['id']) && $this->err(['code' => 1, 'msg' => '数据错误']);
        $row = $this->model->where('id', $param['id'])->find();
        empty($row) && $this->err(['code' => 1, 'msg' => '数据不存在']);
        // 渲染
        $this->app->view->assign('row', $row->getData());
        return $this->app->view->fetch();
    }

    // 保存编辑
    public function save_edit()
    {
        // 验证
        $rule = $this->rule;
        $param = $this->param ?? $this->request->param();
        $rules = [];
        $allow = $this->allow ?: array_keys($param);
        foreach ($rule as $key => $value) {
            [$name,] = explode('|', $key);
            if (in_array($name, $allow)) {
                $rules[$key] = $value;
            }
        }
        $validate = $this->app->validate->rule($rules);
        !$validate->check($param) && $this->errMsg(1, $validate->getError());
        empty($param['id']) && $this->err(['code' => 1, 'msg' => '数据错误']);
        $row = $this->model->where('id', $param['id'])->find();
        empty($row) && $this->err(['code' => 1, 'msg' => '数据不存在']);
        // 操作
        try {
            $save = $row->allowField(array_keys(array_intersect_key($row->getData(), array_flip($allow))))->save($param);
        } catch (\Exception $e) {
            $this->err(['code' => 1, 'msg' => $e->getMessage()]);
        }
        $save ? $this->err(['code' => 0, 'msg' => '保存成功']) : $this->err(['code' => 1, 'msg' => '保存失败']);
    }

    // 删除
    public function del()
    {
        $param = $this->param ?? $this->request->param();
        empty($param['id']) && $this->err(['code' => 1, 'msg' => '请选择数据']);
        $rows = $this->model->whereIn('id', (array)$param['id'])->select();
        $rows->isEmpty() && $this->err(['code' => 1, 'msg' => '数据不存在']);
        try {
            $delete = $rows->delete();
        } catch (\Exception $e) {
            $this->err(['code' => 1, 'msg' => $e->getMessage()]);
        }
        $delete ? $this->err(['code' => 0, 'msg' => '删除成功']) : $this->err(['code' => 1, 'msg' => '删除失败']);
    }

    // 属性修改
    public function modify()
    {
        $param = $this->param ?? $this->request->param();
        empty($param['id']) && $this->err(['code' => 1, 'msg' => '数据错误']);
        $row = $this->model->where('id', $param['id'])->find();
        empty($row) && $this->err(['code' => 1, 'msg' => '数据不存在']);
        // 非法字段
        $field = input('post.field');
        $value = input('post.value');
        (empty($field) || !is_string($field)) && $this->err(['code' => 1, 'msg' => '字段错误']);
        is_null($value) && $this->err(['code' => 1, 'msg' => '值错误']);
        $allow = $this->allow ?: [$field];
        !in_array($field, array_keys(array_intersect_key($row->getData(), array_flip($allow)))) && $this->err(['code' => 1, 'msg' => '该字段不允许修改']);
        // 验证属性
        $data = [];
        $data[$field] = $value;
        $rule = $this->rule;
        $validate_rule = [];
        foreach ($rule as $k => $v) {
            [$prop,] = explode('|', $k);
            if ($prop == $field) {
                $validate_rule[$k] = $v;
            }
        }
        $validates = $this->app->validate->rule($validate_rule);
        !$validates->check($data) && $this->errMsg(1, $validates->getError());
        try {
            $save = $row->save($data);
        } catch (\Exception $e) {
            $this->err(['code' => 1, 'msg' => $e->getMessage()]);
        }
        $save ? $this->err(['code' => 0, 'msg' => '修改成功']) : $this->err(['code' => 1, 'msg' => '修改失败']);
    }

    // 数据关联
    private function datas($datas)
    {
        return $datas;
    }

    // 错误消息
    protected function err($args = ['code' => 1, 'msg' => 'Error!'])
    {
        if ($this->request->isAjax()) {
            echo json_encode($args);
        } else {
            if (!empty($args['msg']))
                echo '<p>&nbsp;</p><p style="text-align:center;color:#393D49;font-size:20px;">' . $args['msg'] . '</p>';
            if (!empty($args['url']))
                echo '<script>
                            setTimeout(function () {
                                var _ta = window.location;
                                if (self.frameElement && self.frameElement.tagName == "IFRAME") {
                                    _ta = parent.window.location;
                                }
                                _ta.href = "' . $args['url'] . '";
                            }, 1000);
                            </script>';
        }
        if (!empty($args['code'])) exit;
    }
}

/**
 * Curd 复用（Db类）
 */
trait DbCurd
{
    protected $module; //应用名称
    protected $controller; //控制器
    protected $action; //方法
    protected $param; //数据

    protected $table = ''; //数据表名
    protected $pk = 'id'; //数据表主键
    protected $field = []; //查询字段
    protected $map = []; //初始条件
    protected $sort = []; //排序
    protected $is_page = true; //是否分页
    protected $rule = [ //验证规则

    ];
    protected $allow_fields = [ //允许的字段
        'save' => [],
        'save_edit' => [],
        'modify' => [],
    ];

    // 列表
    public function index()
    {
        if ($this->request->isAjax()) {
            $param = $this->param ?? $this->request->param();
            // 搜索
            $map = $this->map;
            if (!empty($param['search'])) {
                foreach ((array)$param['search'] as $k => $v) {
                    if (strlen($v)) $map[] = [$k, 'like', "%{$v}%"];
                }
            }
            // 查询
            $list = $this->app->db->table($this->table)->field($this->field)->where($map);
            $data['count'] = $list->count();
            if ($data['count']) {
                $data['code'] = 0;
            } else {
                $data['code'] = 1;
                $data['msg'] = '暂无记录';
            }
            // 分页
            if ($this->is_page) {
                $list = $list->page($param['page'], $param['limit']);
            }
            // 排序
            $list = $list->order($this->sort)->select();
            // 渲染
            $data['data'] = $this->datas($list);
            return $this->err($data);
        }
        return $this->app->view->fetch();
    }

    // 添加
    public function add()
    {
        return $this->app->view->fetch();
    }

    // 保存
    public function save()
    {
        // 验证
        $rule = $this->rule;
        $param = $this->param ?? $this->request->post();
        $rules = [];
        $allow_fields = $this->allow_fields[__FUNCTION__] ?: array_keys($param);
        foreach ($rule as $key => $value) {
            [$name,] = explode('|', $key);
            if (in_array($name, $allow_fields)) {
                $rules[$key] = $value;
            }
        }
        $validate = $this->app->validate->rule($rules);
        !$validate->check($param) && $this->err(['code' => 1, 'msg' => $validate->getError()]);
        // 操作
        $data = $this->request->only($allow_fields);
        try {
            $save = $this->app->db->table($this->table)->insert($data);
        } catch (\Exception $e) {
            $this->err(['code' => 1, 'msg' => $e->getMessage()]);
        }
        $save ? $this->err(['code' => 0, 'msg' => '保存成功']) : $this->err(['code' => 1, 'msg' => '保存失败']);
    }

    // 编辑
    public function edit()
    {
        $param = $this->param ?? $this->request->param();
        empty($param[$this->pk]) && $this->err(['code' => 1, 'msg' => '数据错误']);
        $row = $this->app->db->table($this->table)->where($this->pk, $param[$this->pk])->find();
        empty($row) && $this->err(['code' => 1, 'msg' => '数据不存在']);
        // 渲染
        $this->app->view->assign('row', $row);
        return $this->app->view->fetch();
    }

    // 保存编辑
    public function save_edit()
    {
        // 验证
        $rule = $this->rule;
        $param = $this->param ?? $this->request->post();
        $rules = [];
        $allow_fields = $this->allow_fields[__FUNCTION__] ?: array_keys($param);
        foreach ($rule as $key => $value) {
            [$name,] = explode('|', $key);
            if (in_array($name, $allow_fields)) {
                $rules[$key] = $value;
            }
        }
        $validate = $this->app->validate->rule($rules);
        !$validate->check($param) && $this->err(['code' => 1, 'msg' => $validate->getError()]);
        empty($param[$this->pk]) && $this->err(['code' => 1, 'msg' => '数据错误']);
        $row = $this->app->db->table($this->table)->where($this->pk, $param[$this->pk])->find();
        empty($row) && $this->err(['code' => 1, 'msg' => '数据不存在']);
        // 操作
        $data = $this->request->only(array_keys(array_intersect_key($row, array_flip($allow_fields))));
        try {
            $this->app->db->table($this->table)->where($this->pk, $row[$this->pk])->update($data);
        } catch (\Exception $e) {
            $this->err(['code' => 1, 'msg' => $e->getMessage()]);
        }
        $this->err(['code' => 0, 'msg' => '保存成功']);
    }

    // 删除
    public function del()
    {
        $param = $this->param ?? $this->request->param();
        empty($param[$this->pk]) && $this->err(['code' => 1, 'msg' => '请选择数据']);
        $rows = $this->app->db->table($this->table)->whereIn($this->pk, (array)$param[$this->pk])->select();
        $rows->isEmpty() && $this->err(['code' => 1, 'msg' => '数据不存在']);
        try {
            $delete = $this->app->db->table($this->table)->whereIn($this->pk, (array)$param[$this->pk])->delete();
        } catch (\Exception $e) {
            $this->err(['code' => 1, 'msg' => $e->getMessage()]);
        }
        $delete ? $this->err(['code' => 0, 'msg' => '删除成功']) : $this->err(['code' => 1, 'msg' => '删除失败']);
    }

    // 属性修改
    public function modify()
    {
        $param = $this->param ?? $this->request->post();
        empty($param[$this->pk]) && $this->err(['code' => 1, 'msg' => '数据错误']);
        $row = $this->app->db->table($this->table)->where($this->pk, $param[$this->pk])->find();
        empty($row) && $this->err(['code' => 1, 'msg' => '数据不存在']);
        // 非法字段
        $field = input('post.field');
        $value = input('post.value');
        (empty($field) || !is_string($field)) && $this->err(['code' => 1, 'msg' => '字段错误']);
        is_null($value) && $this->err(['code' => 1, 'msg' => '值错误']);
        $allow_fields = $this->allow_fields[__FUNCTION__] ?: [$field];
        !in_array($field, array_keys(array_intersect_key($row, array_flip($allow_fields)))) && $this->err(['code' => 1, 'msg' => '该字段不允许修改']);
        // 验证属性
        $data = [];
        $data[$field] = $value;
        $rule = $this->rule;
        $validate_rule = [];
        foreach ($rule as $k => $v) {
            [$prop,] = explode('|', $k);
            if ($prop == $field) {
                $validate_rule[$k] = $v;
            }
        }
        $validates = $this->app->validate->rule($validate_rule);
        !$validates->check($data) && $this->errMsg(1, $validates->getError());
        try {
            $this->app->db->table($this->table)->where($this->pk, $row[$this->pk])->update($data);
        } catch (\Exception $e) {
            $this->err(['code' => 1, 'msg' => $e->getMessage()]);
        }
        $this->err(['code' => 0, 'msg' => '修改成功']);
    }

    // 数据关联
    private function datas($datas)
    {
        return $datas;
    }

    // 错误消息
    protected function err($args = ['code' => 1, 'msg' => 'Error!'])
    {
        if ($this->request->isAjax()) {
            echo json_encode($args);
        } else {
            if (!empty($args['msg']))
                echo '<p>&nbsp;</p><p style="text-align:center;color:#393D49;font-size:20px;">' . $args['msg'] . '</p>';
            if (!empty($args['url']))
                echo '<script>
                        setTimeout(function () {
                            var _ta = window.location;
                            if (self.frameElement && self.frameElement.tagName == "IFRAME") {
                                _ta = parent.window.location;
                            }
                            _ta.href = "' . $args['url'] . '";
                        }, 1000);
                        </script>';
        }
        if (!empty($args['code'])) exit;
    }
}
